import abc

from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db import transaction
from django.db.models import Count
from django.shortcuts import render, redirect
from django.urls import reverse

from django.http.response import HttpResponseBadRequest

from algorithm.ahp import *
from algorithm.calculate import VauesCalculator
from algorithm.utils import get_model_structure, get_result_bar
from django.views import View

from .forms import *
from .models import *


class ProjectMetaView(LoginRequiredMixin, View, abc.ABC):

    def __init__(self):
        super().__init__()
        self.context = {'segment': ['project']}


# Create your views here.
class ProjectListView(ProjectMetaView):

    def __init__(self):
        super(ProjectListView, self).__init__()
        self.context["segment"].append("project_list")

    def get(self, request):
        self.context.update({'title': 'Projects',
                             'page_name': '项目列表'})
        user_id = request.user.id
        self.context['projects'] = Project.objects.filter(owner=user_id).order_by("-last_update_datetime")
        return render(request, "project/project_list.html", self.context)


"""
class InstanceListView(LoginRequiredMixin, View):

    def get(self, request):
        pass
"""


class ModelListView(ProjectMetaView):

    def __init__(self):
        super(ModelListView, self).__init__()
        self.context["segment"].append("model")

    def get(self, request):
        self.context.update({'title': 'Models',
                             'page_name': '模型列表',
                             "private_models": Model.objects.filter(creator=request.user.id, type='private'),
                             "public_models": Model.objects.filter(type='public'),
                             })
        return render(request, 'project/model_list.html', self.context)


class AlternativeListView(ProjectMetaView):

    def __init__(self):
        super(AlternativeListView, self).__init__()
        self.context["segment"].append("alternative")

    def get(self, request):
        self.context.update({'title': 'Alternatives',
                             'page_name': '方案列表',
                             'alternatives': Alternative.objects.filter(owner=request.user.id)})
        return render(request, 'project/alternative_list.html', self.context)


class ProjectView(ProjectMetaView):

    def get(self, request, project_id='-1'):
        if project_id == '-1':
            # Create_Post
            self.context.update({'title': 'Projects',
                                 'page_name': '新建项目',
                                 "form": ProjectForm()})
            return render(request, 'project/project_form.html', self.context)
        else:
            # show info
            self.context.update({'title': 'Project Detail',
                                 'page_name': '项目详情',
                                 "project": Project.objects.get(pk=project_id),
                                 "instances": Instance.objects.filter(of_project_id=project_id)})
            return render(request, 'project/project_detail.html', self.context)

    def post(self, request, project_id='-1'):
        project_form = ProjectForm(request.POST)
        if project_form.is_valid():
            project_data = project_form.cleaned_data
            Project.objects.create(name=project_data["name"],
                                   desc=project_data["desc"],
                                   owner_id=request.user.id)
            return redirect(reverse('project_list'))


def instance_detail_view(request, instance_id):
    instance = Instance.objects.get(pk=instance_id)
    context = {"segment": ["project", "instance"]}
    context.update({'title': 'Instance Detail',
                    'page_name': '实例详情',
                    "project": instance.of_project,
                    "instance": instance,
                    "model": instance.model,
                    "alternatives": Alternative.objects.filter(
                        instancehasalternative__instance_id=instance_id),
                    "group": instance.group,
                    "answer": Answer.objects.filter(questionnaire__instance_id=instance_id),
                    "result": Result.objects.filter(instance_id=instance_id).exists(),
                    "questionnaire_existed": Questionnaire.objects.filter(instance_id=instance_id).exists()})
    context.update(get_model_structure(instance.model))
    return render(request, 'project/instance_detail.html', context)


class InstanceView(ProjectMetaView):

    def __init__(self):
        super(InstanceView, self).__init__()
        self.context["segment"].append("instance")

    def get(self, request, project_id, instance_id='-1'):
        # Create_Post
        instance_form = InstanceForm()
        instance_form.base_fields["model"].queryset = Model.objects.filter(creator=request.user.id, type="private")
        self.context.update({'title': 'Instances',
                             'page_name': '新建实例',
                             "form": InstanceForm(),
                             "project_id": project_id})
        return render(request, 'project/instance_form.html', self.context)

    def post(self, request, project_id):
        instance_form = InstanceForm(request.POST)
        if instance_form.is_valid():
            instance_data = instance_form.cleaned_data
            Instance.objects.create(name=instance_data["name"],
                                    desc=instance_data["desc"],
                                    of_project=Project.objects.get(pk=project_id),
                                    model=instance_data["model"],
                                    method=instance_data["method"])
            return redirect(reverse('project_detail', args=(project_id,)))


def get_customized_node_form(model_entry):
    """将子结点非空的结点添加到NodeForm.parent_id的备选项中

    :param model_entry: 模型实体
    :return: 一个定制化的表单
    """
    ahp = AHPModel(model_entry.context)
    customized_form_class = NodeForm
    customized_form_class.base_fields["parent_id"].choices = \
        [(node.id, f"{node.id}：（{node.name}）") for node in ahp.find_nodes_by_type(node_type="leaf", against=True)]
    return {"form": customized_form_class()}


class ModelView(ProjectMetaView):

    def __init__(self):
        super(ModelView, self).__init__()
        self.context["segment"].append("model")

    def get(self, request, model_id='-1'):
        if model_id == '-1':
            # Create_Post
            self.context.update({'title': 'Models',
                                 'page_name': '新建模型',
                                 "form": ModelForm()})
            return render(request, 'project/model_form.html', self.context)
        else:
            model = Model.objects.get(pk=model_id)
            self.context.update({'title': 'Models',
                                 'page_name': '模型详情',
                                 'model': model,
                                 'instances': Instance.objects.filter(model=model)
                                .annotate(Count('result', distinct=True),
                                          Count('alternative', distinct=True)),
                                 'group': model.group})
            self.context.update(get_model_structure(model))
            self.context.update(get_customized_node_form(model))
            return render(request, 'project/model_detail.html', self.context)

    def post(self, request, model_id='-1'):
        model_form = ModelForm(request.POST)
        if model_form.is_valid():
            model_data = model_form.cleaned_data
            Model.objects.create(name=model_data["name"],
                                 desc=model_data["desc"],
                                 context=model_data["context"],
                                 creator_id=request.user.id)
            return redirect(reverse('model_list'))


def generate_public_model_from_private_view(request, model_id):
    model = Model.objects.get(pk=model_id)
    if model.type == 'private':
        new_model = Model.objects.create(
            name=model.name,
            context=model.context,
            creator=request.user,
            desc=f"{model.desc}\nfrom private model {model_id}",
            type='public'
        )
        return redirect(reverse('model_detail', args=(new_model.id,)))
    else:
        return HttpResponseBadRequest("模型类型必须为私有!")


def generate_private_model_from_public_view(request, model_id):
    model = Model.objects.get(pk=model_id)
    if model.type == 'public':
        new_model = Model.objects.create(
            name=model.name,
            context=model.context,
            creator=request.user,
            desc=f"{model.desc}\nfrom public model {model_id}",
            type='private'
        )
        return redirect(reverse('model_detail', args=(new_model.id,)))
    else:
        return HttpResponseBadRequest("模型类型必须为公共!")


class AlternativeView(ProjectMetaView):

    def __init__(self):
        super(AlternativeView, self).__init__()
        self.context["segment"].append("alternative")

    def get(self, request, alter_id='-1'):
        if alter_id == '-1':
            # Create_Post
            self.context.update({'title': 'Alternatives',
                                 'page_name': '新建方案',
                                 "form": AlternativeForm()})
            return render(request, 'project/alternative_form.html', self.context)
        else:
            self.context.update({'title': 'Alternatives',
                                 'page_name': '方案详情'})
            self.context["alternative"] = Alternative.objects.get(pk=alter_id)
            self.context["instances"] = Instance.objects.filter(instancehasalternative__alternative_id=alter_id)
            return render(request, 'project/alternative_detail.html', self.context)

    def post(self, request, alter_id='-1'):
        alternative_form = AlternativeForm(request.POST)
        if alternative_form.is_valid():
            alternative_data = alternative_form.cleaned_data
            Alternative.objects.create(name=alternative_data["name"],
                                       desc=alternative_data["desc"],
                                       owner_id=request.user.id)
            return redirect(reverse('alternative_list'))


@login_required
def edit_alternative(request, instance_id):
    # 如果发布问卷或数据已有，不添加表单
    # 添加表单
    context = {"segment": ["project", "instance"],
               'title': 'Add Alternatives',
               'page_name': '添加方案',
               "instance": Instance.objects.get(pk=instance_id),
               "alternatives": Alternative.objects.filter(instancehasalternative__instance_id=instance_id)}
    if not Questionnaire.objects.filter(instance_id=instance_id).exists():
        context["alternative_to_select"] = (Alternative.objects.filter(owner=request.user.id)
                                            .exclude(instancehasalternative__instance_id=instance_id))
        context["selectable"] = True
    else:
        context["selectable"] = False

    return render(request, "project/alternative_select.html", context)


@transaction.atomic
@login_required
def add_alternative(request, instance_id, alternative_id):
    # 如果发布问卷或者数据已有，404

    # 否则添加方案
    InstanceHasAlternative.objects.get_or_create(instance_id_id=instance_id, alternative_id_id=alternative_id)

    return redirect(reverse('alternative_instance_list', args=(instance_id,)))


@transaction.atomic
@login_required
def delete_alternative(request, instance_id, alternative_id):
    # 如果发布问卷或者数据已有，404

    # 否则删除方案
    InstanceHasAlternative.objects.get(instance_id=instance_id, alternative_id=alternative_id).delete()

    return redirect(reverse('alternative_instance_list', args=(instance_id,)))


@login_required
def get_instance_result_view(request, instance_id):
    result_queryset = Result.objects.filter(instance_id=instance_id)
    if result_queryset.exists():
        result = result_queryset[0]
    else:
        cal = VauesCalculator(instance_id)
        res = cal.calculate()
        result = Result.objects.create(instance_id=instance_id, result=res)
    return redirect(reverse('result_detail', args=(result.id,)))


@login_required
def result_list_view(request):  # TODO 增加对FAHP结果的支持
    context = {"segment": ["project", "result"],
               'title': 'Result Management',
               'page_name': '结果管理',
               "results": Result.objects.filter(instance__of_project__owner=request.user)}

    return render(request, "project/result_list.html", context)


@login_required
def result_detail_view(request, result_id):  # TODO 增加对FAHP提示的支持
    context = {"segment": ["project", "result"],
               'title': 'Result Detail',
               'page_name': '结果详情', }
    result = Result.objects.get(pk=result_id)
    ahp_model = AHPModel(result.instance.model.context)
    alternatives = Alternative.objects.filter(instancehasalternative__instance_id=result.instance)
    alter_id2name = {str(q["id"]): q["name"] for q in alternatives.values("id", "name")}
    criteria_id2name = {c_id: ahp_model.find_node_by_id(c_id)["name"] for c_id in
                        list(result.result["criteria"].keys())}
    context["alternatives"] = alternatives
    context["result"] = result
    alternative_names = [alter_id2name[a_id] for a_id in list(result.result["criteria"].values())[0].keys()]

    context["result_context"] = {k: {"name": criteria_id2name[k], "values": v} for k, v in
                                 result.result["criteria"].items()}
    criteria_result = [[v["name"]] + list(v["values"].values()) for v in context["result_context"].values()]
    context["bar"] = get_result_bar(alternative_names, criteria_result)

    if result.instance.method == "AHP":
        pass
    elif result.instance.method == "FAHP":
        detail = dict()
        for key, value in list(result.result.items())[1:]:
            result_context = {k: {"name": criteria_id2name[k], "values": v} for k, v in
                              value.items()}
            criteria_result = [[v["name"]] + list(v["values"].values()) for v in result_context.values()]
            detail[key] = {
                "result_context": result_context,
                "bar": get_result_bar(alternative_names, criteria_result)
            }
        context["detail"] = detail

    return render(request, "project/result_detail.html", context)


@login_required
def project_index_view(request):
    context = {'title': 'Project Management', 'segment': ['project'], 'page_name': '项目管理',
               'project_count': Project.objects.filter(owner=request.user).count(),
               'model_count': Model.objects.filter(creator=request.user).count(),
               'instance_count': Instance.objects.filter(of_project__owner=request.user).count(),
               'alternative_count': Alternative.objects.filter(owner=request.user).count(),
               'result_count': Result.objects.filter(instance__of_project__owner=request.user).count(), }

    return render(request, 'project/index.html', context)
